package com.antoco.controlcenter.comm;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;

import com.antoco.controlcenter.comm.codec.DistanceByte2MessageDecoder;
import com.antoco.controlcenter.iface.IDataReceiveListener;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Consumer;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.Schedulers;

/**
 * Created by MQjehovah on 20200714
 * Version : v1.0
 * Function: tcp client
 */
public class DistanceTcpClient {

    String TAG_log = "COMM";
    private Socket mSocket;

    private OutputStream mOutputStream;
    private InputStream mInputStream;


    private DistanceByte2MessageDecoder decoder;
    private CompositeDisposable mCompositeDisposable;
    private IDataReceiveListener onDataReceiveListener = null;

    public void setOnDataReceiveListener(
            IDataReceiveListener dataReceiveListener) {
        onDataReceiveListener = dataReceiveListener;
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    starReadRunable();
                    break;
            }
        }
    };

    public DistanceTcpClient() {
        decoder = new DistanceByte2MessageDecoder();
        mCompositeDisposable = new CompositeDisposable();
    }


    public void starReadRunable() {
        Observable<Long> observable = Observable.interval(0, 500, TimeUnit.MILLISECONDS).doOnNext(aLong -> readMessage());
        DisposableObserver<Long> disposableObserver = getDisposableObserver();
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
        mCompositeDisposable.add(disposableObserver);
    }

    private void readMessage() {
        try {
            if (mInputStream == null) return;
            byte[] buffer = new byte[mInputStream.available()];
            int size = mInputStream.read(buffer);//null data -1 , zrd serial rule size default 10
            if (size == -1) {
            }
            if (size > 0) {
                List<Object> frameList = (List<Object>) decoder.decode(buffer);
                if (frameList != null) {
                    for (Object message : frameList) {
                        message = (byte[]) message;
                        onDataReceiveListener.onDataReceive(message);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }

    public void connect(String ip, int port) {
        startSocketRunable(ip, port);
    }

    private void socketConnet(String ip, int port) {
        try {
            if (mSocket != null) {
                mSocket.close();
                mSocket = null;
            }

            InetAddress ipAddress = InetAddress.getByName(ip);
            mSocket = new Socket(ipAddress, port);

            //设置不延时发送
            mSocket.setTcpNoDelay(true);
            //设置输入输出缓冲流大小
            mSocket.setSendBufferSize(8 * 1024);
            mSocket.setReceiveBufferSize(8 * 1024);
            if (isConnect()) {
                mOutputStream = mSocket.getOutputStream();
                mInputStream = mSocket.getInputStream();
                mHandler.sendEmptyMessage(1); //通信启动通知
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void startSocketRunable(String ip, int port) {
        Observable<Long> observable = Observable.interval(0, 500, TimeUnit.MILLISECONDS).take(3).doOnNext(aLong -> socketConnet(ip, port));
        DisposableObserver<Long> disposableObserver = getDisposableObserver();
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
        mCompositeDisposable.add(disposableObserver);
    }

    private DisposableObserver<Long> getDisposableObserver() {

        return new DisposableObserver<Long>() {

            @Override
            public void onNext(Long aLong) {
            }

            @Override
            public void onError(Throwable throwable) {
            }

            @Override
            public void onComplete() {
            }
        };
    }


    public void clear() {
        mHandler.removeCallbacksAndMessages(null);
        if (null == mCompositeDisposable) {
            return;
        }
        mCompositeDisposable.clear();
    }

    /**
     * socket is connect
     */
    public boolean isConnect() {
        boolean flag = false;
        if (mSocket != null) {
            flag = mSocket.isConnected();
        }
        return flag;
    }

    /**
     * socket disconnect
     */
    public void disconnect() {

        clear();
        try {
            if (mOutputStream != null) {
                mOutputStream.close();
            }

            if (mInputStream != null) {
                mInputStream.close();
            }

            if (mSocket != null) {
                mSocket.close();
                mSocket = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * send byte[] cmd
     * Exception : android.os.NetworkOnMainThreadException
     */
    public void sendByteCmd(final byte[] mBuffer) {
        if (isConnect() && mOutputStream != null) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        if (mOutputStream != null) {
                            mOutputStream.write(mBuffer);
                            mOutputStream.flush();
                        }
                    } catch (IOException e) {
                        disconnect();
                        // mHandler.sendMessage(Message.obtain(mHandler, -1, "发送消息失败"));
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

    /**
     * send string cmd to serial
     */
    public void sendStrCmds(String cmd) {
        byte[] mBuffer = cmd.getBytes();
        sendByteCmd(mBuffer);
    }


    /**
     * send prt content cmd to serial
     */
    public void sendChsPrtCmds(String content) {
        try {
            byte[] mBuffer = content.getBytes("GB2312");
            sendByteCmd(mBuffer);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

}